home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / 4cmp22o.zip / MULTID.4TH < prev    next >
Text File  |  1994-08-13  |  16KB  |  597 lines

  1. \ ForthCMP  Multitasking Module
  2. \ Copyright 1985, 1993 (C) By Thomas Almy.  All rights reserved.
  3.  
  4. \ Permission is granted to registered users of ForthCMP to sell or distribute
  5. \ computer programs incorporating the compiled contents of this file.
  6.  
  7. \ This module writes direct to the display for terminal I/O
  8.  
  9.  
  10. .( LOADING MULTID) CR
  11. INCLUDE INTS
  12. INCLUDE FARMEM1
  13. 10 HEX
  14.  
  15. \ If EGA is defined non-zero then 43 line EGA code is generated
  16. FIND EGA #IF DROP #ELSE 0 CONSTANT EGA #THEN
  17.  
  18. \ If VGA is defined non-zero then 50 line VGA code is generated
  19. FIND VGA #IF DROP #ELSE 0 CONSTANT VGA #THEN
  20.  
  21. EGA 0<> VGA 0<> OR CONSTANT ENHANCED
  22.  
  23. ENHANCED #IF 0 CONSTANT VID-DELAY #THEN  \ no vid delay if EGA or VGA
  24. ENHANCED 0=  #IF VARIABLE crtport  3D4 crtport ! #THEN
  25.  
  26. \ If VID-DELAY is defined non-zero then anti-snow code is added
  27. FIND VID-DELAY #IF DROP #ELSE 0 CONSTANT VID-DELAY #THEN
  28.  
  29. VARIABLE vidseg     \ VIDEO SEGMENT
  30. B800 vidseg !
  31. 50 CONSTANT c/l     \ Characters per line
  32. EGA #IF 2B #ELSE VGA #IF 32 #ELSE 19 #THEN #THEN
  33.    CONSTANT l/s     \ lines per screen
  34.  
  35.  
  36. DECIMAL  
  37. 0 0 IN/OUT NEED SINGLE 
  38. 0 0 IN/OUT NEED MULTI
  39. 0 0 IN/OUT NEED PAUSE
  40. 0 0 IN/OUT NEED end-timer
  41. 0 0 IN/OUT NEED start-timer
  42. 0 0 IN/OUT NEED CLS
  43.  
  44.  
  45. VARIABLE ?multi         \ true if multitasking turned on
  46. VARIABLE user           \ disp into user segment--used at comp time
  47. VARIABLE CTASK          \ pointer to task list
  48. VARIABLE inexpect       \ executing EXPECT -- only one at a time, please!
  49.  
  50.  \ Semaphores
  51.  
  52. 1 0 IN/OUT
  53. : SEMA BEGIN DUP @ WHILE PAUSE REPEAT ON ;
  54.  
  55. 1 0 IN/OUT
  56. : PHORE  OFF PAUSE ;
  57.  
  58.  
  59. 0 0 IN/OUT 
  60. : BYE  unsetup-vid end-timer bye ;
  61.  
  62.  \ Memory management interface
  63. 1 1 IN/OUT
  64. : GET malloc IF    ." OUT OF MEMORY " BYE THEN ;
  65.  
  66.  \ USER VARIABLES 
  67. H: UALLOT  DSEG user @  +  user ! ;
  68. 1 2 IN/OUT
  69. H: UCREATE user @ CONSTANT ;
  70. H: UVARIABLE UCREATE 2 UALLOT ;
  71. H: URESET DSEG  0 user ! ;
  72. URESET
  73.  \ redefinition of primitive I/O functions
  74. HEX
  75. 1 0 IN/OUT
  76. : storecursor ( DISPL -- )  CTASK @ 12 + CS: ! ;
  77.  
  78. 1 0 IN/OUT
  79. : setcursor (  DISPL -- )  
  80. ENHANCED #IF
  81.     2/ DUP 0F 3D4 PC! 3D5 PC! >< 0E 3D4 PC! 3D5 PC! 
  82. #ELSE
  83.     2/ DUP 0F crtport @ PC! crtport @ 1+ PC!
  84.     >< 0E crtport @ PC! crtport @ 1+ PC! 
  85. #THEN
  86. ;
  87.  
  88. 0 0 IN/OUT
  89. : nocursor  l/s c/l * 2* 1- setcursor ( OFF SCREEN ! ) ;
  90.  
  91. 2 0 IN/OUT
  92. : GOTOXY  c/l * + 2*  storecursor ;
  93.  
  94.  
  95. ENHANCED #IF
  96. 0 0 IN/OUT
  97. EGA #IF
  98. CODE set-ega
  99.     03 # AX MOV  10 INT                     \ SET MODE 3
  100.     1112 # AX MOV  0 # BL MOV  10 INT       \ Load 8X8 font
  101.     1200 # AX MOV  20 # BL MOV  10 INT      \ Load new printscreen
  102.     1 # AH MOV  707 # CX MOV  10 INT        \ LOAD CURSOR SCAN LINES
  103.     3D4 # DX MOV  0A # AL MOV  [DX] BYTE OUT \ set cursor 
  104.     FWD, THEN,
  105.     DX INC
  106.     6 # AL MOV  [DX] OUT
  107.     RET
  108. END-CODE
  109. #ELSE  \ must be VGA
  110. CODE set-ega
  111.     1202 # AX MOV 30 # BL MOV 10 INT  \ 400 scan lines
  112.     03 # AX MOV  10 INT                     \ SET MODE 3
  113.     1112 # AX MOV  0 # BL MOV  10 INT       \ Load 8X8 font
  114.     1200 # AX MOV  20 # BL MOV  10 INT      \ Load new printscreen
  115.     RET
  116. END-CODE
  117. #THEN
  118.  
  119. 0 0 IN/OUT
  120. CODE unset-ega
  121. VGA #IF
  122.     1201 # AX MOV 30 # BL MOV 10 INT  \ 350 scan lines
  123. #THEN
  124.     03 # AX MOV  10 INT  RET  END-CODE
  125. #THEN 
  126.  
  127. 0 0 IN/OUT
  128. : setup-vid
  129. ENHANCED #IF
  130.     set-ega
  131.     CTASK @ 12 + CS: OFF    \ home cursor
  132. #ELSE
  133.     40 49 C@L 7 = IF 3B4 crtport ! B000 vidseg ! THEN \ MONOCHROME
  134.     40 50 C@L 40 51 C@L GOTOXY
  135.     vidseg @  c/l l/s 1- * 2* 1+ C@L  CTASK @ 14 + CS: ! 
  136. #THEN
  137. ;
  138.  
  139.  CODE unsetup-vid  
  140. ENHANCED #IF
  141.     CALL' CLS
  142.     CALL' unset-ega
  143.     DX DX XOR
  144. #ELSE
  145.     CTASK [] BX MOV
  146.     CS: 12 +[BX] AX MOV  \ cursor offset
  147.     c/l # BX MOV 
  148.     DX DX XOR
  149.     AX 1 SAR  
  150.     BX IDIV
  151.     AL DH MOV  
  152. #THEN
  153.     2 # AH MOV 
  154.     BH BH XOR  
  155.     10 INT  
  156.     RET 
  157. END-CODE \ unsetup-vid
  158.  
  159. CODE scrmove  ( source dest wordCount -- )
  160.     BX POP 
  161.     CX POP
  162.     DI POP
  163.     SI POP
  164.     LOOP IF,
  165.         DS PUSHSEG
  166. VID-DELAY #IF  
  167.         B800 # vidseg [] CMP  =0 IF,
  168.             3DA # DX MOV
  169.             BEGIN,  
  170.                 BYTE [DX] IN  
  171.                 8 # AL TEST  
  172.             =0 ~ UNTIL,
  173.             DX DEC
  174.             DX DEC
  175.             21 # AL MOV
  176.             BYTE [DX] OUT
  177.         THEN, 
  178. #THEN
  179.         vidseg [] AX MOV
  180.         AX DS >SEG
  181.         AX ES >SEG
  182.         REPZ MOVS
  183.         DS POPSEG
  184. VID-DELAY #IF
  185.         B800 # vidseg [] CMP  =0 IF,
  186.             3D8 # DX MOV
  187.             29 # AL MOV
  188.             BYTE [DX] OUT
  189.         THEN, 
  190. #THEN
  191.     THEN, 
  192.     BX JMPI 
  193. END-CODE \ scrmove
  194.  
  195. 2 0 IN/OUT
  196. CODE scrfill ( source wordCount -- )
  197.     vidseg [] ES >SEG
  198.     20 # BYTE ES: [BX] MOV
  199.     CTASK [] DI MOV
  200.     CS: 14 +[DI] CL MOV  \ style
  201.     CL ES: 1 +[BX] MOV
  202.     BX PUSH
  203.     BX INC 
  204.     BX INC 
  205.     BX PUSH  
  206.     AX DEC 
  207.     AX PUSH
  208.     CALL' scrmove
  209.     RET
  210. END-CODE \ scrfill
  211.  
  212. 0 0 IN/OUT
  213. : scrollup  c/l 2*  0  c/l l/s 1- * scrmove
  214.     c/l l/s 1- * 2*  c/l    scrfill
  215.     c/l l/s 1- * 2*  CTASK @ 12 + CS: ! ( set cursor ) ;
  216.  
  217. 0 2 IN/OUT
  218. : ?XY     CTASK @ 12 + CS: @  2/  0 c/l UM/MOD ;
  219.  
  220. 1 0 IN/OUT
  221. : FOREGROUND 0F AND CTASK @ 14 + TUCK CS: @ F0 AND OR SWAP CS: ! ;
  222.  
  223. 1 0 IN/OUT
  224. : BACKGROUND 7 AND 4 << CTASK @ 14 + TUCK CS: @ 0F AND OR SWAP CS: ! ;
  225.  
  226.  
  227. : EMIT  
  228.     CTASK @ 12 + CS: @  c/l l/s * 2* >= IF scrollup THEN
  229.     vidseg @ CTASK @ 12 + CS: @ C!L
  230.     CTASK @ 14 + CS: @ vidseg @ CTASK @ 12 + CS: @ 1+ C!L
  231.     CTASK @ 12 + CS: @ 2+ storecursor  PAUSE ;
  232.  
  233. : CR
  234.     CTASK @ 12 + CS: @  
  235.     c/l 2*  U/  1+  c/l 2*  *
  236.     DUP c/l l/s * 2* = IF DROP scrollup  CTASK @ 12 + CS: @ THEN
  237.     storecursor  PAUSE ;
  238.  
  239. : SPACES
  240.     DUP 0> IF
  241.         c/l l/s * 2*  CTASK @ 12 + CS: @ - OVER 2* < IF ( too big )
  242.         0 DO BL EMIT LOOP ELSE
  243.             CTASK @ 12 + CS: @  SWAP 2DUP scrfill
  244.         2* + storecursor  PAUSE 
  245.         THEN 
  246.     ELSE   DROP
  247.     THEN
  248. ;
  249.  
  250.  
  251. 2 1 IN/OUT
  252. CODE (type) ( AX has count, BX has string, result is cursor position )
  253.     BX SI MOV
  254.     CTASK [] BX MOV
  255.     CS: 12 +[BX] DI MOV \ cursor
  256.     AX CX MOV
  257.     CS: 14 +[BX] AH MOV \ style
  258.     vidseg [] ES >SEG
  259.     LOOP IF, 
  260.         BEGIN,
  261.             BYTE LODS
  262.             STOS  
  263.         LOOP ~ UNTIL,
  264.     THEN,
  265.     DI AX MOV       \ final cursor position
  266.     RET
  267. END-CODE \ (type)
  268.  
  269. : TYPE 
  270.     c/l l/s * 2*  CTASK @ 12 + CS: @ - OVER 2* < IF ( too big )
  271.         0 ?DO COUNT EMIT LOOP DROP
  272.     ELSE 
  273.         (type) storecursor PAUSE 
  274.     THEN ;
  275.  
  276. 2 1 IN/OUT
  277. CODE (cs:type) ( AX has count, BX has string, result is cursor position)
  278.     BX SI MOV
  279.     CTASK [] BX MOV
  280.     CS: 12 +[BX] DI MOV \ cursor
  281.     AX CX MOV
  282.     CS: 14 +[BX] AH MOV \ style
  283.     vidseg [] ES >SEG
  284.     LOOP IF, 
  285.         BEGIN,
  286.             CS: BYTE LODS
  287.             STOS  
  288.         LOOP ~ UNTIL,
  289.     THEN,
  290.     DI AX MOV       \ final cursor position
  291.     RET
  292. END-CODE \ (cs:type)
  293.  
  294. : CS:TYPE 
  295.     c/l l/s * 2* CTASK @ 12 + CS: @ - OVER 2* < IF ( too big )
  296.         0 ?DO CS: COUNT EMIT LOOP DROP
  297.     ELSE 
  298.         (cs:type) storecursor PAUSE 
  299.     THEN ;
  300.  
  301.  
  302. 0 0 IN/OUT 
  303. : CLS  0  c/l l/s *  scrfill  0 storecursor ;
  304.  
  305. 0 1 IN/OUT
  306. CODE ?TERMINAL 
  307.     CALL' PAUSE     \ allow another task to execute
  308.     1 # AH MOV 
  309.     16 INT 
  310.     0 # AX MOV
  311.     =0 ~ IF, AX DEC  THEN,
  312.     RET
  313. END-CODE \ ?TERMINAL
  314.  
  315. : PAD CTASK @ 16 + CS: @ ;
  316.  
  317.  
  318. : KEY  BEGIN ?TERMINAL  CTASK @ 12 + CS: @ setcursor UNTIL  
  319.     0 8 BDOS 
  320.     PAUSE
  321.     nocursor ;
  322.  
  323.  \ EXPECT
  324. FIND SPAN #IF DROP #ELSE VARIABLE SPAN #THEN
  325.  
  326. 0 0 IN/OUT
  327. : bu  CTASK @ 12 + CS: @ 2- DUP storecursor BL EMIT storecursor -1 SPAN +! ;
  328.  
  329. DECIMAL
  330.  
  331. : EXPECT
  332.     inexpect SEMA       \ too hard if two or more tasks want input at once!
  333.     SPACE
  334.     >R SPAN OFF
  335.     BEGIN
  336.         SPAN @ R@ < WHILE       \ more room on line
  337.         KEY  CASE
  338.         27 OF BEGIN SPAN @ 0> WHILE bu REPEAT  ENDOF
  339.         8  OF SPAN @ 0> IF bu THEN ENDOF
  340.         13 OF BL EMIT
  341.               R> DROP DROP 
  342.               inexpect PHORE 
  343.               EXIT ENDOF
  344.         ( ELSE ) DUP EMIT 
  345.                  OVER SPAN @ + C! 
  346.                  1 SPAN +!
  347.         0 ENDCASE
  348.     REPEAT
  349.     inexpect PHORE
  350.     R> 2DROP ;
  351.  
  352.  
  353.  \ TASK CREATION 
  354. HEX
  355. H: TASK                          \ values after INIT-TASKS:
  356.    CSEG FORCE CREATE HERE E92E , \ DISP 0 -- JMP ( task asleep )
  357.    DSEG CTASK @ ,  CTASK !    \     02 -- relative addr nxt task
  358.    user @ ,                   \     04 -- size of user area (not used?)
  359.    0 ,                        \     06 -- SS register contents
  360.    user @ pssize 10 * + ,     \     08 -- SP register contents
  361.    user @ pssize 10 * + rssize + , \     0A -- BP register contents
  362.    ,                          \     0C -- PC contents
  363. \ the following fields are for per-task variables
  364. \ and could be selectively elimiated if not needed if space is 
  365. \ at a premium.  In that case, offsets may need to be adjusted
  366. \ for words which use latter fields.
  367.    0 ,                        \     0E -- Message list
  368.    0 ,                        \     10 -- Timer
  369.    0 ,                        \     12 -- Cursor location
  370.    7 ,                        \     14 -- character attribute (style)
  371.    DSEG HERE 80 ALLOT 20 + ,  \     16 -- PAD, a per-task work area
  372. 0 #IF
  373. Initially, DISP 2 has absolute address of next task.
  374. This values as well as DISP 6 get
  375. filled in by INIT-TASKS when application is run.
  376. #THEN
  377.  
  378. CSEG FORCE  HERE  CREATE MAIN-TASK  \ Give it a name
  379. DSEG CTASK !                    \ Task list points to it
  380. 80CD ,                          \ DISP 0 -- INT 80 (task awake)
  381.    0 ,                          \ 02 -- relative addr next task
  382.    0 ,                          \ 04 -- NOT USED
  383.    0 ,                          \ 06 -- SS register contents
  384.    0 ,                          \ 08 -- SP register contents
  385.    0 ,                          \ 0A -- BP register contents
  386.    0 ,                          \ 0C -- PC contents
  387.    0 ,                          \ 0E -- Message list
  388.    0 ,                          \ 10 -- Timer
  389.    0 ,                          \ 12 -- Cursor Location
  390.    7 ,                          \ 14 -- Style
  391.    DSEG HERE 80 ALLOT 20 + ,    \ 16 -- PAD, a per-task work area
  392. 0 #IF
  393. DISP-2, 6, and 12 get filled in by INIT-TASK.  -8 -0A and -0C
  394. are filled by first task swap (which is done by INIT-TASK).
  395. #THEN
  396.  
  397.  \ TASK INITIALIZATION
  398. 0 0 IN/OUT 
  399. : INIT-TASKS \ This MUST be executed to start multitasking
  400.     CTASK @
  401.     BEGIN ?DUP WHILE  \ for each task DO:
  402.         2+ DUP CS: @ IF  \ one follows, this isnt main task
  403.             DUP 8 + CS: @ 10 + 4 >>  GET 
  404.          OVER 4 + CS: ! \ stackseg
  405.             DUP CS: @ TUCK   \ next task
  406.         ELSE
  407.             0 SWAP CTASK @ \ next task is head of list
  408.         THEN
  409.         OVER - 2- SWAP CS: !  
  410.     REPEAT
  411.     MAIN-TASK CTASK !  
  412.     setup-vid
  413.     ?SS: MAIN-TASK 6 + CS: !    \ sets main task stack segment
  414.     start-timer
  415.     MULTI ( GO!!! ) ;
  416.  
  417.  \ TASK DISPATCHER
  418. CODE PAUSE  
  419.     0 # ?multi [] CMP  
  420.     =0 IF, RET THEN,
  421.     CTASK [] BX MOV         \ current task
  422.     CS: 0C +[BX] POP        \ save PC
  423.     BP CS: 0A +[BX] MOV     \ save BP
  424.     SP CS: 08 +[BX] MOV     \ save SP
  425.     CS: 2 +[BX] BX ADD  
  426.     4 # BX ADD  
  427.     CLI                \ no ints during dispatch!
  428.     BX JMPI  ( dispatch )
  429. END-CODE \ PAUSE
  430.  
  431. 0 #IF
  432. Tasks are linked together so that jumping to a task will cause
  433. jumping to the next if it is asleep, or doing an INT 80 if it
  434. is awake.  Thanks to Henry Laxen's Forth 83 model for the
  435. technique.
  436. #THEN
  437.  
  438. L: start-task ( the INT80 routine )  
  439.     BX POP 
  440.     BX DEC 
  441.     BX DEC                  \ Pointer to the task
  442.     CS: 6 +[BX] SS >SEG     \ restore stack segment
  443.     CS: 8 +[BX] SP MOV      \ restore SP
  444.     STI                     \ Interrupts are safe now
  445.     CS: 0A +[BX] BP MOV     \ restore BP
  446.     BX  CTASK [] MOV        \ current task
  447.     CS: 0C +[BX] JMPI       \ go!
  448. FORTH \ start-task 
  449. 0 #IF
  450. This code starts up a new task by setting up all registers,
  451. fixing CTASK and USERP, and jumping to where we left off.
  452. #THEN
  453.  
  454.  \ TASK MANAGEMENT
  455. : SINGLE  ?multi OFF ;
  456.  
  457. : MULTI   ?multi ON
  458.     ?CS: start-task 80 set-handler  \ install interrupt vector
  459.     PAUSE  \ start with a task swap
  460. ;
  461.  
  462. 1 0 IN/OUT
  463. : WAKE  80CD CS: <- ;
  464.  
  465. 1 0 IN/OUT
  466. \ the 2e prefix byte (CS override) makes the jmp instruction 4 bytes long
  467. : SLEEP (  task -- )   E92E CS: <- ;
  468.  
  469. 1 1 IN/OUT
  470. : WAITING?  10 + CS: @ 0<> ;
  471.  
  472. 0 0 IN/OUT
  473. : STOP  CTASK @ SLEEP PAUSE ;
  474.  
  475. 0 1 IN/OUT
  476. : ACTIVE-TASKS
  477.     0 MAIN-TASK
  478.     BEGIN
  479.         DUP WAITING? IF SWAP 1+ SWAP ELSE 
  480.             DUP CS: @ 80CD = IF SWAP 1+ SWAP THEN THEN \ check for active
  481.         DUP 2+ CS: @ + 4 + \ address of next task
  482.     DUP MAIN-TASK = UNTIL     \ Loop until back to start
  483.     DROP ( task address )
  484. ;
  485.  
  486.  \ MESSAGE PASSING
  487. 0 1 IN/OUT
  488. : MESSAGE?  CTASK @ 0E + CS: @ ;
  489.  
  490. 0 1 IN/OUT
  491. : GET-MESSAGE  
  492.   BEGIN MESSAGE? ?DUP 0= WHILE STOP REPEAT
  493.   DUP  0 @L  CTASK @ 0E + CS: !  \ Unlink message
  494. ;   
  495.  
  496. 1 1 IN/OUT
  497. : MESSAGES 
  498.     0 SWAP 0E + CS: @ ?DUP IF
  499.         BEGIN SWAP 1+ SWAP  0 @L  ?DUP 0= UNTIL
  500.     THEN ;
  501.  
  502. 2 0 IN/OUT
  503. : SEND-MESSAGE 
  504.     OVER 0 SWAP 0 !L        \ set message's next field to NIL
  505.     DUP WAITING? NOT IF DUP WAKE THEN \ fire up receiving task
  506.                                 \ unless waiting for timer
  507.     0E + DUP CS: @ ?DUP IF  \ Existing messages in queue
  508.         NIP
  509.         BEGIN DUP 0 @L ?DUP WHILE NIP REPEAT \ find end of list
  510.         0 !L  \ store message at end of list
  511.     ELSE
  512.         CS: !     \ no existing messages, put at head of queue.
  513.     THEN
  514.     PAUSE ;  \ Give it a chance to run
  515.  
  516.  \ control-break handler
  517. \ always gets control and (currently) dumps task information
  518.  
  519. 2VARIABLE cb_save
  520.  
  521. 1B CONSTANT cb_int
  522.  
  523. 0 0 IN/OUT
  524. : cbt  
  525.     CLS 
  526.     SINGLE
  527.     end-timer
  528.     ." Task statistics: "
  529.     MAIN-TASK \ start with first
  530.     BEGIN CR
  531.         HEX DUP 0 <# # # # # #> TYPE SPACE \ address
  532.         DUP WAITING? IF ." Waiting " DUP 10 + CS: @ . ." ticks" ELSE 
  533.             DUP CS: @ 80CD = IF ." Active" ELSE ." Sleeping" THEN THEN 
  534.         DUP 2+ CS: @ + 4 + \ address of next task
  535.     DUP MAIN-TASK = UNTIL     \ Loop until back to start
  536.     DROP ( task address )
  537. EGA #IF
  538.     CR ." Hit any key when finished"    KEY DROP
  539. #THEN
  540.     unsetup-vid
  541.     bye
  542. ;
  543.  
  544.  
  545. ' cbt TASK cb-task
  546.  
  547.  
  548. L: cb_handler ( actual interrupt handler )
  549.       80CD # CS: cb-task [] MOV \ wake cb task
  550.     STI
  551.     IRET FORTH
  552.  
  553.  
  554.  \ timer
  555. 1C CONSTANT t_int               \ timer interupt vector number
  556. CSEG FORCE 
  557. CREATE t_save 4 ALLOT           \ original interupt vector
  558. L: t_handler
  559.     PUSHF CS: t_save CALLF    \ do original functions
  560.     BX PUSH
  561.     MAIN-TASK # BX MOV ( start of list )
  562.     BEGIN,  
  563.         CS: 0 # 10 +[BX] CMP =0 ~ IF, ( non_zero time )
  564.             CS: 10 +[BX] DEC  ( count down )
  565.             =0 IF, 80CD # CS: [BX] MOV THEN, ( wake task )
  566.         THEN,
  567.         CS: 2 +[BX] BX ADD 
  568.         4 # BX ADD ( next task )
  569.         MAIN-TASK # BX CMP  
  570.     =0 UNTIL, ( back at start? )
  571.     BX POP 
  572.     IRET
  573. FORTH \ t_handler
  574.  
  575. \ timer start and end                          08:09 11/18/85
  576.  
  577. : start-timer  \ and control break handler
  578.     t_int get-handler  t_save CS: 2!
  579.     ?CS: t_handler t_int set-handler
  580.     cb_int get-handler cb_save 2!
  581.     ?CS: cb_handler cb_int set-handler
  582. ;
  583.  
  584. : end-timer
  585.     t_save CS: 2@  t_int set-handler
  586.     cb_save 2@ cb_int set-handler
  587. ;
  588.  
  589. 2 0 IN/OUT
  590. : TIME-OUT ( ticks task -- )  DUP SLEEP 10 + CS: ! ;
  591.  
  592. 1 0 IN/OUT
  593. : WAIT ( ticks -- ) CTASK @ TIME-OUT PAUSE ;
  594.  
  595. DSEG 0A = #IF DECIMAL #THEN
  596.